package puzzle.common.view.swing;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.Reader;

import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;

import puzzle.common.data.AbstractBoard;
import puzzle.common.loader.AbstractProblemDictionary;

public abstract class AbstractPuzzleSolverApp<B extends AbstractBoard<B>> extends JFrame {
	private static final long serialVersionUID = 1L;

	private PuzzleSolverAppPanel<B> m_appPanel;

	private JFileChooser m_fileChooser;

	private JMenuBar m_menuBar;
	private JMenu m_fileMenu;
	private JMenu m_solveMenu;
	private JMenu m_optionMenu;
	private AbstractButton m_helpMenu;
	private JMenuItem m_openFileMenuItem;
	private JMenu m_openBuiltinMenu;
	private JMenuItem m_exitMenuItem;
	private JMenuItem m_rerunMenuItem;
	private JMenuItem m_pauseMenuItem;
	private JMenuItem m_stopMenuItem;
	private JMenu m_speedOptionMenu;
	private JMenu m_detailOptionMenu;
	private JMenuItem m_aboutMenuItem;

	private ButtonGroup m_speedOptionGroup;
	private ButtonGroup m_detailOptionGroup;

	private PuzzleSolverAppMetadata<B> m_metadata;
	private AbstractProblemDictionary m_builtinProblemDictionary;

	public AbstractPuzzleSolverApp(PuzzleSolverAppMetadata<B> metadata) {
		m_metadata = metadata;
		m_builtinProblemDictionary = metadata.createBuiltinProblemDictionary();
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		initComponents();
	}

	//
	// Initialize components
	//

	private void initComponents() {
		setTitle(m_metadata.getTitle());

		setLayout(new BorderLayout());

		m_appPanel = new PuzzleSolverAppPanel<B>(m_metadata);
		m_appPanel.addPropertyChangeListener(new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent evt) {
				appPanel_propertyChange(evt);
			}
		});
		add(m_appPanel, BorderLayout.CENTER);

		initMenuBar();
		add(m_menuBar, BorderLayout.NORTH);

		try {
			m_fileChooser = new JFileChooser();
		} catch (SecurityException ex) {
			m_openFileMenuItem.setEnabled(false);
		}
	}

	private void initMenuBar() {
		m_menuBar = new JMenuBar();

		// File

		m_fileMenu = new JMenu("File");
		m_fileMenu.setMnemonic('F');

		m_openFileMenuItem = new JMenuItem("Open file...", 'O');
		m_openFileMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				openFile();
			}
		});
		m_fileMenu.add(m_openFileMenuItem);

		if (m_builtinProblemDictionary != null) {
			m_openBuiltinMenu = new JMenu("Open built-in");
			m_openBuiltinMenu.setMnemonic('B');
			for (String problemName : m_builtinProblemDictionary.getProblemNames()) {
				JMenuItem mi = new JMenuItem(problemName);
				mi.setActionCommand(problemName);
				mi.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						openBuiltin(e.getActionCommand());
					}
				});
				m_openBuiltinMenu.add(mi);
			}
			m_fileMenu.add(m_openBuiltinMenu);
		}

		m_exitMenuItem = new JMenuItem("Exit", 'X');
		m_exitMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				exit();
			}
		});
		m_fileMenu.add(m_exitMenuItem);

		// Solve

		m_solveMenu = new JMenu("Solve");
		m_solveMenu.setMnemonic('S');

		m_rerunMenuItem = new JMenuItem("Rerun", 'R');
		m_rerunMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				m_appPanel.startSolver();
			}

		});

		m_pauseMenuItem = new JMenuItem("Pause/Resume", 'P');
		m_pauseMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				m_appPanel.togglePauseSolver();
			}
		});

		m_stopMenuItem = new JMenuItem("Stop", 'S');
		m_stopMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				m_appPanel.stopSolver();
			}
		});

		m_solveMenu.add(m_rerunMenuItem);
		m_solveMenu.add(m_pauseMenuItem);
		m_solveMenu.add(m_stopMenuItem);

		// Option

		m_optionMenu = new JMenu("Option");
		m_optionMenu.setMnemonic('O');

		m_speedOptionMenu = new JMenu("Speed");
		m_speedOptionMenu.setMnemonic('S');
		m_speedOptionGroup = new ButtonGroup();

		final String[] speedOptionMenuCaptions = { "Fast", "Medium", "Slow" };
		final String[] speedOptionMenuTimes = { "10", "50", "500" };
		final boolean[] speedOptionMenuSelected = { true, false, false };
		for (int i = 0; i < 3; i++) {
			String t = speedOptionMenuCaptions[i];
			JMenuItem mi = new JRadioButtonMenuItem(t);
			mi.setMnemonic(t.charAt(0));
			mi.setActionCommand(speedOptionMenuTimes[i]);
			mi.setSelected(speedOptionMenuSelected[i]);
			mi.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					m_appPanel.setSpeed(Integer.parseInt(e.getActionCommand()));
				}
			});
			m_speedOptionMenu.add(mi);
			m_speedOptionGroup.add(mi);
		}

		m_detailOptionMenu = new JMenu("Detail");
		m_detailOptionMenu.setMnemonic('D');
		m_detailOptionGroup = new ButtonGroup();

		final String[] detailOptionMenuCaptions = { "None", "Less", "More", "Most" };
		final boolean[] detailOptionMenuSelected = { false, true, false, false };
		for (int i = 0; i < 4; i++) {
			String t = detailOptionMenuCaptions[i] + "(" + i + ")";
			JMenuItem mi = new JRadioButtonMenuItem(t);
			mi.setMnemonic('0' + i);
			mi.setActionCommand(String.valueOf(i));
			mi.setSelected(detailOptionMenuSelected[i]);
			mi.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					m_appPanel.setDetailLevel(Integer.parseInt(e.getActionCommand()));
				}
			});
			m_detailOptionMenu.add(mi);
			m_detailOptionGroup.add(mi);
		}

		m_optionMenu.add(m_speedOptionMenu);
		m_optionMenu.add(m_detailOptionMenu);

		// Help

		m_helpMenu = new JMenu("Help");
		m_helpMenu.setMnemonic('H');

		m_aboutMenuItem = new JMenuItem("About...", 'A');
		m_aboutMenuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				showAboutBox();
			}
		});
		m_helpMenu.add(m_aboutMenuItem);

		m_menuBar.add(m_fileMenu);
		m_menuBar.add(m_solveMenu);
		m_menuBar.add(m_optionMenu);
		m_menuBar.add(m_helpMenu);

	}

	//
	// Actions
	//

	private void openFile() {
		if (m_fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
			m_appPanel.openFile(m_fileChooser.getSelectedFile());
		}
	}

	private void openBuiltin(String problemName) {
		Reader r;
		try {
			r = m_builtinProblemDictionary.getProblemData(problemName);
		} catch (IOException ex) {
			m_appPanel.setStatusMessage("Failed to open the problem data." + ex);
			ex.printStackTrace();
			return;
		}
		m_appPanel.openReader(r);
	}

	private void exit() {
		System.exit(0);
	}

	private void showAboutBox() {
		JOptionPane.showMessageDialog(this, m_metadata.getTitle() + "\n"
				+ "by Tomoto Shimizu Washio\n" + "(c) 2009 All rights reserved", m_metadata
				.getTitle(), JOptionPane.INFORMATION_MESSAGE);
	}

	//
	// Event handlers
	//

	private void appPanel_propertyChange(PropertyChangeEvent evt) {
		m_rerunMenuItem.setEnabled(m_appPanel.isBoardRead() && m_appPanel.isAvailable());
		m_pauseMenuItem.setEnabled(m_appPanel.isBoardRead() && !m_appPanel.isAvailable());
		m_stopMenuItem.setEnabled(m_appPanel.isBoardRead() && !m_appPanel.isAvailable());
	}

}
